﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Web;
using suanpan.API.Components.Core;
using suanpan.API.Components.Exceptions;
//using MySpace.Applications;
//using MySpace.Services.Core;
//using MySpace.Common.Services.ApiFacade;
//using MySpace.Services.Core.Context;
//using MySpace.Services.Exceptions;
//using MySpace.Services.Utilities;
//using AuthConstants = MySpace.Security.Services.Authorization.Constants;


namespace suanpan.API.Components.OAuth
{
    public class OAuthManager
    {
        #region CTor

        public OAuthManager()
        {
            this.OAuthSigner = new OAuthDigitalSigner();
            int configuredDuration;
            timestampExpirationMinutes = int.TryParse(ConfigurationManager.AppSettings["OAuthExpirationTime"],
                out configuredDuration) ? configuredDuration : 15;
        }

        #endregion

        #region Properties

        protected bool isValid = false;
        protected int timestampExpirationMinutes;
        public string ConsumerKey { get; set; }
        protected bool IsTimestampExpired(int epochTime, int expirationMinutes)
        {
            return (DateTimeExtensions.UtcNowEpoch() > epochTime + expirationMinutes * 60);
        }
        public OAuthDigitalSigner OAuthSigner { get; set; }
        

        #endregion

        #region Static Methods

        public static bool IsOAuth(OAuthContext context)
        {
            if (context.QueryParameters.AllKeys.Contains<string>(Parameters.OAuth_Consumer_Key))
            {
                return true;
            }
            else if (!string.IsNullOrEmpty(context.AuthorizationHeader) && 
                context.AuthorizationHeader.ToUpper().StartsWith(Parameters.OAuth_Authorization_Header))
            {
                return true;
            }
            return false;
        }

        

        #endregion

        #region Public Methods


        public bool IsAuthenticated(OAuthContext context, out string exceptionMessage)
        {
            exceptionMessage = string.Empty;

            this.OAuthSigner = CreateDigitalSigner(context);

            string oauth_signature = this.OAuthSigner.RequestParameters[Parameters.OAuth_Signature].Replace(" ", "+");

            string serverDigitalSignature = this.OAuthSigner.DigitalSignature;

            if (!oauth_signature.Equals(serverDigitalSignature))
            {
                exceptionMessage = "Invalid digital signature.";
                return false;
            }
            
            
            if (IsTimestampExpired(int.Parse(this.OAuthSigner.Timestamp), timestampExpirationMinutes))
            {
                exceptionMessage = "Digital signature has expired.";
                return false;
            }
           
            //nonce logic would go here.

            isValid = true;
            return isValid;
        }

       
        #endregion

        #region Private Methods

        private OAuthDigitalSigner CreateDigitalSigner(OAuthContext context)
        {

            OAuthDigitalSigner digitalSigner = new OAuthDigitalSigner();

            if (context.RequestUri == null)
                throw new Exception("RequestUri does not exist.");

            digitalSigner.RequestParameters = new NameValueCollection();
            
            if (!string.IsNullOrEmpty(context.AuthorizationHeader))//Authenticating via Auth header.
            {
                string filteredList = context.AuthorizationHeader.Substring(context.AuthorizationHeader.IndexOf("OAuth ") + 6).Trim();
                string[] parameters = filteredList.Split(',');

                foreach (string parameter in parameters)
                {
                    string[] pSegments = parameter.Split('=');
                    pSegments[1] = parameter.Replace(string.Format("{0}=", pSegments[0].Trim()), string.Empty).Replace("\"", "");

                    digitalSigner.RequestParameters.Add(pSegments[0], HttpUtility.UrlDecode(pSegments[1]));
                }
            }
            digitalSigner.AddParameterSet(context.QueryParameters);

            //TODO: do checks on actual parameter values.
            if (!digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Consumer_Key))
                throw new BadRequestException(string.Format("Missing required parameter : {0}", Parameters.OAuth_Consumer_Key));
            if (!digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Token))
                throw new BadRequestException(string.Format("Missing required parameter : {0}", Parameters.OAuth_Token));
            if (!digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Signature))
                throw new BadRequestException(string.Format("Missing required parameter : {0}", Parameters.OAuth_Signature));
            if (!digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Signature_Method))
                throw new BadRequestException(string.Format("Missing required parameter : {0}", Parameters.OAuth_Signature_Method));
            if (!digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Timestamp))
                throw new BadRequestException(string.Format("Missing required parameter : {0}", Parameters.OAuth_Timestamp));
            if (!digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Nonce))
                throw new BadRequestException(string.Format("Missing required parameter : {0}", Parameters.OAuth_Nonce));
            //if (!digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Version))
            //    throw new BadRequestException(string.Format("Missing required parameter : {0}", Parameters.OAuth_Version));

            digitalSigner.RequestUrl = string.Format("http://{0}{1}", context.RequestUri.Authority, context.RequestUri.AbsolutePath);
            digitalSigner.ConsumerKey = digitalSigner.RequestParameters[Parameters.OAuth_Consumer_Key];
            digitalSigner.ConsumerSecret = GetConsumerSecret(digitalSigner.ConsumerKey);
            digitalSigner.Nonce = digitalSigner.RequestParameters[Parameters.OAuth_Nonce];
            digitalSigner.SigningMethod = GetSignatureMethod(digitalSigner.RequestParameters[Parameters.OAuth_Signature_Method]);
            digitalSigner.Timestamp = digitalSigner.RequestParameters[Parameters.OAuth_Timestamp];
            digitalSigner.Token = digitalSigner.RequestParameters[Parameters.OAuth_Token];
            if (digitalSigner.RequestParameters.AllKeys.Contains<string>(Parameters.OAuth_Version))
                digitalSigner.Version = digitalSigner.RequestParameters[Parameters.OAuth_Version];
            digitalSigner.RequestMethod = context.RequestMethod;
            digitalSigner.CreateDigitalSignature();
            
            return digitalSigner;
        }

        private SignatureMethod GetSignatureMethod(string name)
        {
            switch (name)
            {
                case "HMAC-SHA1":
                    return SignatureMethod.HMAC_SHA1;
                case "RSA-SHA1":
                    return SignatureMethod.RSA_SHA1;
                default:
                    return SignatureMethod.PLAINTEXT;
            }
        }

        private string GetConsumerSecret(string consumerKey)
        {
            return RestChessContext.ConsumerProvider.ConsumerGet(consumerKey).ConsumerSecret;
        }

        
        #endregion

    }
}
